
#include <BFont>
#include <BPainter>
#include <BEvent>
#include <BMouseEvent>
#include <BChangeEvent>
#include <BStyle>

#include "IComboBoard.h"
#include <BComboBox>
#include "member_BComboBox.h"

using namespace BWE;

#define member					(*(member_BComboBox*)_ptr)
#define member_allocate()		_ptr = new member_BComboBox(this)
#define member_release()		delete (member_BComboBox*)_ptr

BComboBox::BComboBox(BWidget* parent) : BWidget(parent)
{
	member_allocate();
	this->setPerch(Part_Right, 23);
}
BComboBox::~BComboBox()
{
	member_release();
}

void BComboBox::setAlign(Align align)
{
	if (member.align != align)
	{
		member.align = align;
		this->fresh();
	}
}
Align BComboBox::align() const
{
	return member.align;
}

void BComboBox::addItem(const BString& text, const BValue& value)
{
	ComboItem item;
	item.text = text;
	item.value = value;
	member.items.append(item);
	member.board->dirty = true;
	member.dirty = true;
	this->fresh();
	setCurrent(0);
}
void BComboBox::addItem(BIcon* icon, const BString& text, const BValue& value)
{
	ComboItem item;
	item.icon = icon;
	item.text = text;
	item.value = value;
	member.items.append(item);
	member.board->dirty = true;
	member.dirty = true;
	this->fresh();
	setCurrent(0);
}

void BComboBox::insertItem(int pos, const BString& text, const BValue& value)
{
	if (member.items.insert(pos, ComboItem()))
	{
		ComboItem& item = member.items[pos];
		item.text = text;
		item.value = value;
		member.board->dirty = true;
		if (member.current >= pos)
		{
			emit(Signal_CurrentChanged, member.current);
			member.dirty = true;
			this->fresh();
		}
	}
}
void BComboBox::insertItem(int pos, BIcon* icon, const BString& text, const BValue& value)
{
	if (member.items.insert(pos, ComboItem()))
	{
		ComboItem& item = member.items[pos];
		item.icon = icon;
		item.text = text;
		item.value = value;
		member.board->dirty = true;
		if (member.current >= pos)
		{
			emit(Signal_CurrentChanged, member.current);
			member.dirty = true;
			this->fresh();
		}
	}
}

void BComboBox::removeItem(const BString& text)
{
	for(int i = 0; i < member.items.size(); i++)
	{
		ComboItem& item = member.items[i];
		if(item.text == text)
		{
			member.items.remove(i);
			member.board->dirty = true;
			if (member.current == i)
				member.current = -1;
			break;
		}
	}
	setCurrent(0);
}
void BComboBox::removeItem(int index)
{
	if(0 <= index && index < member.items.size())
	{
		member.items.remove(index);
		member.board->dirty = true;
	}
}

void BComboBox::clearItems()
{
	if (member.items.size())
	{
		member.items.clear();
		member.current = -1;
		member.board->dirty = true;
		this->fresh();
	}
}

void BComboBox::setItem(int index, const BString& text)
{
	if (member.items.check(index))
	{
		ComboItem& item = member.items[index];
		item.text = text;
	}
}
void BComboBox::setItem(int index, BIcon* icon, const BString& text)
{
	if (member.items.check(index))
	{
		ComboItem& item = member.items[index];
		item.icon = icon;
		item.text = text;
	}
}

void BComboBox::setItemValue(int index, const BValue& value)
{
	if (member.items.check(index))
	{
		ComboItem& item = member.items[index];
		item.value = value;
	}
}
void BComboBox::setItemValue(const BString& text, const BValue& value)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		ComboItem& item = member.items[i];
		if (item.text == text)
		{
			item.value = value;
		}
	}
}

int BComboBox::findValue(const BValue& value) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		ComboItem& item = member.items[i];
		if (item.value == value)
			return i;
	}
	return -1;
}
int BComboBox::findText(const BString& text) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		ComboItem& item = member.items[i];
		if (item.text == text)
			return i;
	}
	return -1;
}

int BComboBox::itemCount() const
{
	return member.items.size();
}
BIcon* BComboBox::itemIcon(int index) const
{
	if (member.items.check(index))
	{
		ComboItem& item = member.items[index];
		return item.icon;
	}
	return 0;
}
const BString& BComboBox::itemText(int index) const
{
	ComboItem& item = member.items(index);
	return item.text;
}

bool BComboBox::raiseItem(int index)
{
	if (member.items.move(index, 0))
	{
		if (member.current == index || member.current == 0)
		{
			emit(Signal_CurrentChanged, member.current);
			member.dirty = true;
			this->fresh();
		}
		return true;
	}
	return false;
}
bool BComboBox::raiseItem(const BValue& value)
{
	int index = findValue(value);
	return raiseItem(index);
}
bool BComboBox::raiseItem(const BString& text)
{
	int index = findText(text);
	return raiseItem(index);
}

bool BComboBox::lowerItem(int index)
{
	if (member.items.move(index, member.items.size()))
	{
		if (member.current >= index)
		{
			emit(Signal_CurrentChanged, member.current);
			member.dirty = true;
			this->fresh();
		}
		return true;
	}
	return false;
}
bool BComboBox::lowerItem(const BValue& value)
{
	int index = findValue(value);
	return lowerItem(index);
}
bool BComboBox::lowerItem(const BString& text)
{
	int index = findText(text);
	return lowerItem(index);
}

bool BComboBox::moveItemint(int index, int pos)
{
	return false;
}
bool BComboBox::moveItemint(const BValue& value, int pos)
{
	return false;
}
bool BComboBox::moveItemint(const BString& text, int pos)
{
	return false;
}

void BComboBox::setCurrent(int current)
{
	if (member.current != current && member.items.check(current))
	{
		member.current = current;
		emit(Signal_CurrentChanged, current);
		this->fresh();
	}
}
int BComboBox::current() const
{
	return member.current;
}

void BComboBox::setCurrentText(const BString& text)
{
	for(int i = 0; i < member.items.size(); i++)
	{
		ComboItem& item = member.items[i];
		if(item.text == text)
		{
			setCurrent(i);
			break;
		}
	}
}
const BString& BComboBox::currentText() const
{
	ComboItem& item = member.items(member.current);
	return item.text;
}

void BComboBox::setCurrentValue(const BValue& value)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		ComboItem& item = member.items[i];
		if (item.value == value)
		{
			setCurrent(i);
			break;
		}
	}
}
const BValue& BComboBox::currentValue() const
{
	ComboItem& item = member.items(member.current);
	return item.value;
}

void BComboBox::setIconSize(const BSize& iconSize)
{
	if (member.iconSize != iconSize)
	{
		member.iconSize = iconSize;
	}
}
const BSize& BComboBox::iconSize() const
{
	return member.iconSize;
}

BSize BComboBox::sizeHint() const
{
	BSize hsize = member.iconSize;
	const BFont* font = this->font();
	if(font)
	{
		hsize.width() += font->width() * 6;
		hsize.height() = bMax(hsize.height(), font->height());
	}
	return this->shellSize() + hsize;
}

void BComboBox::resizeEvent(const BEvent& event)
{
	member.dirty = true;
}

void BComboBox::styleEvent(const BEvent& event)
{
	const BStyle* style = this->realStyle();
	if (style)
	{
		member.arrowIcon = style->icon("arrow-down");
	}
	else
	{
		member.arrowIcon = 0;
	}
	member.dirty = true;
	this->fresh();
}
void BComboBox::freshEvent(const BEvent& event)
{
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
}
void BComboBox::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();
	painter.drawContent();

	if (member.current > -1)
	{
		const ComboItem& item = member.items[member.current];
		if(item.icon)
		{
			painter.drawImage(member.iconRect, item.icon->image(State_Normal));
		}
		if (painter.setColor(Color_Text))
		{
			const BFont* font = painter.font();
			if (font)
			{
				BSize size = font->textSize(item.text);
				BRect rect = member.textRect.align(member.align, size);
				painter.drawText(rect, item.text, false);
			}
		}
	}

	painter.drawMargin();
	painter.drawPerch();

	if (member.arrowIcon)
	{
		BImage* image = member.arrowIcon->image(member.arrowState);
		if (image)
		{
			painter.drawImage(member.arrowRect, image);
		}
	}
	else
	{
		StyleRole colorRole = Color_Arrow;
		if (member.arrowState == State_Pressed)
			colorRole = Color_Arrow_Pressed;
		else if (member.arrowState == State_Hovered)
			colorRole = Color_Arrow_Hovered;
		if (painter.setColor(colorRole))
		{
			painter.fillTriangle(member.arrowTriangle[0], member.arrowTriangle[1], member.arrowTriangle[2]);
		}
	}

	painter.drawBorder();
}

void BComboBox::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	member.arrowState = State_Normal;
}
void BComboBox::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	member.arrowState = State_Normal;
	fresh();
}

void BComboBox::mousePressEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	State state = member.arrowRect.contain(pos) ? State_Pressed : State_Normal;
	if (member.arrowState != state)
	{
		member.arrowState = state;
		member.dirty = true;
		fresh();
	}
}
void BComboBox::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	if (member.arrowState == State_Pressed)
	{
		member.arrowState = member.arrowRect.contain(pos) ? State_Hovered : State_Normal;
		fresh();
	}
}
void BComboBox::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	State state = member.arrowRect.contain(pos) ? State_Hovered : State_Normal;
	if (member.arrowState != state)
	{
		member.arrowState = state;
		fresh();
	}
}

void BComboBox::mouseClickEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	if (member.arrowRect.contain(pos))
	{
		if(member.board->visible())
			member.board->hide();
		else
			member.board->popup();
	}
}
void BComboBox::mouseDoubleClickEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	if (clientRect().contain(pos))
	{
		if (member.board->visible())
			member.board->hide();
		else
			member.board->popup();
	}
}

